package com.zhaokuner.parentstarter.ctrladaptor;

import com.zhaokuner.parentstarter.common.ResultData;
import com.zhaokuner.parentstarter.ctrladaptor.annotation.CtrlAdaptorService;
import com.zhaokuner.parentstarter.ctrladaptor.http.CtrlAdaptorReqEntryClientInfo;
import com.zhaokuner.parentstarter.ctrladaptor.http.ICtrlAdapterHttpClient;
import com.zhaokuner.parentstarter.ctrladaptor.model.CtrlAdaptorReqEntry;
import com.zhaokuner.parentstarter.ctrladaptor.model.ResultDataCtrlAdaptor;
import com.zhaokuner.parentstarter.resultcode.SuperResultCodeUtil;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 * Description:代理
 * <p>
 * Copyright: 天津联想协同科技有限公司
 * <p>
 *
 * @author 赵希坤 zhaoxikun
 * @date 2021/2/11 13:32
 */
@Log4j2
public class CtrlAdaptorInvocationHandler implements InvocationHandler {
    /**
     * 缓存
     */
    private ConcurrentHashMap<Method, MethodHandle> methodHandleMap = new ConcurrentHashMap<>(8);

    private static ICtrlAdapterHttpClient ctrlAdapterHttpClient;

    @Autowired
    public void setCtrlAdapterHttpClient(ICtrlAdapterHttpClient ctrlAdapterHttpClient) {
        CtrlAdaptorInvocationHandler.ctrlAdapterHttpClient = ctrlAdapterHttpClient;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object obj = null;
        if (method.isDefault()) {
            //接口中的default 方法，default方法没有直接可以调用的方法
            MethodHandle defaultMethodHandle = methodHandleMap.computeIfAbsent(method, key -> {
                try {
                    Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
                            .getDeclaredConstructor(Class.class, int.class);
                    constructor.setAccessible(true);
                    Class<?> declaringClass = method.getDeclaringClass();
                    int allModes = MethodHandles.Lookup.PUBLIC | MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE;
                    return constructor.newInstance(declaringClass, allModes)
                            .unreflectSpecial(method, declaringClass)
                            .bindTo(proxy);
                } catch (Exception e) {
                    log.error("AdaptorInvocationHandler invoke error:" + method.getName(), e);
                }
                return null;
            });
            obj = defaultMethodHandle.invokeWithArguments(args);
        } else {
            obj = method.invoke(proxy, args);
        }
        //判断是否是目标代理
        CtrlAdaptorService ctrlAdaptorServiceName = method.getDeclaringClass().getAnnotation(CtrlAdaptorService.class);
        if (null == ctrlAdaptorServiceName) {
            return obj;
        }
        if (null == obj) {
            log.info("AdaptorInvocationHandler invoke return null:" + method.getName());
            return ResultData.error(SuperResultCodeUtil.DATA_NOT_FOUND);
        }
        if (!(obj instanceof ResultDataCtrlAdaptor)) {
            //返回的不是 需要http请求的数据
            return obj;
        }
        CtrlAdaptorReqEntry ctrlAdaptorReqEntry = ((ResultDataCtrlAdaptor) obj).getCtrlAdaptorReqEntry();
        if (null == ctrlAdaptorReqEntry) {
            return obj;
        }
        CtrlAdaptorReqEntryClientInfo clientInfo = new CtrlAdaptorReqEntryClientInfo(ctrlAdaptorReqEntry);

        //设置名称
        clientInfo.setServiceName(ctrlAdaptorServiceName.name());
        //设置返回值的类型
        Type returnType = method.getGenericReturnType();
        clientInfo.setReturnType(returnType);
        //根据url model 远程请求数据
        return ctrlAdapterHttpClient.execute(clientInfo);

    }
}
